package com.charles.video.common.security.service;

import com.charles.video.api.domain.SysMember;
import com.charles.video.api.model.MemberLogin;
import com.charles.video.common.core.constant.CacheConstants;
import com.charles.video.common.core.constant.SecurityConstants;
import com.charles.video.common.core.exception.ServiceException;
import com.charles.video.common.core.utils.IpUtils;
import com.charles.video.common.core.utils.JwtUtils;
import com.charles.video.common.core.utils.StringUtils;
import com.charles.video.common.core.utils.uuid.IdUtils;
import com.charles.video.common.redis.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author Charles-H
 * 
 * 令牌业务层
 */
@Component
public class TokenService {
    
    @Autowired
    private RedisService redisService;

    protected static final long MILLIS_SECOND = 1000;

    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    private final static long expireTime = CacheConstants.EXPIRATION;

    private final static String ACCESS_TOKEN = CacheConstants.LOGIN_TOKEN_KEY;

    private final static Long MILLIS_MINUTE_TEN = CacheConstants.REFRESH_TIME * MILLIS_MINUTE;

    /** 创建令牌 */
    public Map<String, Object> createToken(MemberLogin memberLogin) {
        String token = IdUtils.fastUUID();
        
        SysMember member = memberLogin.getMember();
        Integer memberId = member.getMember_id();
        String memberNum = member.getMember_num();
        String role = member.getRole();
        
        memberLogin.setMemberId(memberId);
        memberLogin.setMemberNum(memberNum);
        memberLogin.setToken(token);
        memberLogin.setRole(role);
        
        RequestAttributes attr = RequestContextHolder.getRequestAttributes();
        memberLogin.setIpaddr(IpUtils.getIpAddr(((ServletRequestAttributes) attr).getRequest()));
        refreshToken(memberLogin);

        // Jwt
        Map<String, Object> claimsMap = new HashMap<>();
        claimsMap.put(SecurityConstants.MEMBER_KEY, token);
        claimsMap.put(SecurityConstants.MEMBER_ID, memberId);
        claimsMap.put(SecurityConstants.MEMBER_NUM, memberNum);
        claimsMap.put(SecurityConstants.MEMBER_ROLE, role);

        // 接口返回
        Map<String, Object> rspMap = new HashMap<>();
        rspMap.put("access_token", JwtUtils.createToken(claimsMap));
        rspMap.put("expires_in", expireTime);
        return rspMap;
    }

    /** 刷新令牌 */
    public void refreshToken(MemberLogin memberLogin) {
        memberLogin.setLoginTime(System.currentTimeMillis());
        memberLogin.setExpireTime(memberLogin.getLoginTime() + expireTime * MILLIS_MINUTE);
        
        String memberKey = getTokenKey(memberLogin.getToken());
        redisService.setCacheObject(memberKey, memberLogin, expireTime, TimeUnit.MINUTES);
    }

    /** 获取令牌标识 */
    private String getTokenKey(String token) {
        return ACCESS_TOKEN + token;
    }
    
    /** 验证令牌有效期 */
    public void verifyToken(MemberLogin memberLogin) {
        Long expireTime = memberLogin.getExpireTime();
        long currentTimeMillis = System.currentTimeMillis();
        if (expireTime - currentTimeMillis <= MILLIS_MINUTE_TEN) refreshToken(memberLogin);
    }
    
    /** 通过令牌获取成员信息 */
    public MemberLogin getMemberLogin(String token) {
        MemberLogin member = null;
        try {
            if (StringUtils.isNotEmpty(token)) {
                String memberKey = JwtUtils.getMemberKey(token);
                member = redisService.getCacheObject(getTokenKey(memberKey));
                return member;
            }
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        return member;
    }

    /** 删除用户信息 */
    public void deleteMemberLogin(String token) {
        if (StringUtils.isNotEmpty(token)) {
            String memberKey = JwtUtils.getMemberKey(token);
            redisService.deleteObject(getTokenKey(memberKey));
        }
    }
}
